﻿/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：实现横向或纵向滑动选择的功能
* CreateTime：2020-05-19 15:04:17
* Version：1.0
* Modify Recorder：
*************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ScrollSelect : ScrollRect
{
    public enum AxialType
    {
        Horizontal,
        Vertical
    }

    [Serializable]
    public class UnityEventInt : UnityEvent<int>
    {
    }

    public AxialType axialType;
    public int defaultPageIndex;
    public float smooth = 0.2f;
    public UnityEvent onDragPages;
    public UnityEventInt onPageChanged;

    private int _totalPage;
    private bool _draging;
    private bool _smoothMoving;
    private bool _isInitialized;
    private ScrollPageItem _curSelectedItem;
    private int _curPageIndex;
    private Vector2 _endPos = Vector2.zero;
    private List<ScrollPageItem> _pageItemList;
    
    public int TotalPage => _totalPage;
    public int CurPageIndex => _curPageIndex;
    public ScrollPageItem CurSelectedItem => _curSelectedItem;
    public List<ScrollPageItem> PageItems => _pageItemList;

    protected override void Awake()
    {
        if (axialType == AxialType.Horizontal)
        {
            horizontal = true;
            vertical = false;
        }
        else
        {
            horizontal = false;
            vertical = true;
        }

        base.Awake();
        if (Application.isPlaying && !_isInitialized)
        {
            _isInitialized = true;
            Init();
        }
    }

    private void Init()
    {
        _totalPage = content.childCount;
        _curPageIndex = -1;
        _pageItemList = new List<ScrollPageItem>();
        foreach (RectTransform child in content)
        {
            ScrollPageItem item = child.GetComponent<ScrollPageItem>();
            if (item == null)
                item = child.gameObject.AddComponent<ScrollPageItem>();
            item.SetScrollSelect(this);
            item.pageIndex = child.GetSiblingIndex();
            _pageItemList.Add(item);

            Vector3 localPos = child.localPosition;
            localPos.z = item.pageIndex;
            item.transform.localPosition = localPos;
        }

        LayoutGroup group = transform.GetComponentInChildren<LayoutGroup>();
        if (group)
            group.enabled = false;
        
        PageTo(defaultPageIndex);
    }

    public override void OnBeginDrag(PointerEventData eventData)
    {
        if (Input.touchCount > 1) return;
        Graphic graphic = GetComponent<Graphic>();
        if (graphic == null || graphic.raycastTarget == false) return;
        if (eventData.button != PointerEventData.InputButton.Left) return;

        _draging = true;
        base.OnBeginDrag(eventData);
    }

    public override void OnDrag(PointerEventData eventData)
    {
        if (Input.touchCount > 1) return;
        Graphic graphic = GetComponent<Graphic>();
        if (graphic && graphic.raycastTarget)
            base.OnDrag(eventData);
    }

    public override void OnEndDrag(PointerEventData eventData)
    {
        if (Input.touchCount > 1) return;
        Graphic graphic = GetComponent<Graphic>();
        if (graphic == null || graphic.raycastTarget == false) return;
        if (eventData.button != PointerEventData.InputButton.Left) return;

        _draging = false;
        _smoothMoving = true;
        base.OnEndDrag(eventData);

        CalculateSelectedItem();
    }

    protected override void LateUpdate()
    {
        if (_draging)
        {
            base.LateUpdate();
            
            CalculateSelectedItem();
            onDragPages?.Invoke();
        }
        else if (_smoothMoving)
        {
            SmoothScroll();
        }
    }

    private void CalculateSelectedItem()
    {
        //比较所有的item到中心点的距离
        float minDis = float.MaxValue;
        ScrollPageItem tempItem = null;
        foreach (RectTransform child in content)
        {
            float dis = Mathf.Abs(GetAxialValue(child.anchoredPosition) + GetAxialValue(content.anchoredPosition));
            if (dis < minDis)
            {
                minDis = dis;
                tempItem = child.GetComponent<ScrollPageItem>();
            }
        }

        if (_curSelectedItem != tempItem)
        {
            _curSelectedItem = tempItem;
            _endPos = content.anchoredPosition;
            _endPos = SetAxialValue(_endPos, -GetAxialValue(_curSelectedItem.GetComponent<RectTransform>().anchoredPosition));
            if (Mathf.Abs(GetAxialValue(_endPos) - GetAxialValue(content.anchoredPosition)) > 1f)
            {
                _smoothMoving = true;
                _curPageIndex = _curSelectedItem.pageIndex;
                onPageChanged.Invoke(_curPageIndex);
            }
        }
    }

    private void SmoothScroll()
    {
        if (Mathf.Abs(GetAxialValue(content.anchoredPosition) - GetAxialValue(_endPos)) > 1f)
        {
            Vector2 pos = Vector2.Lerp(content.anchoredPosition, _endPos, smooth);
            SetContentAnchoredPosition(pos);
        }
        else
        {
            SetContentAnchoredPosition(_endPos);
            _smoothMoving = false;
        }
    }

    //获取当前轴向的值
    private float GetAxialValue(Vector2 pos)
    {
        return axialType == AxialType.Horizontal ? pos.x : pos.y;
    }

    //设置当前轴向的值
    private Vector2 SetAxialValue(Vector2 pos, float value)
    {
        return axialType == AxialType.Horizontal ? new Vector2(value, pos.y) : new Vector2(pos.x, value);
    }
    
    private ScrollPageItem GetPageItem(int pageIndex)
    {
        foreach (var item in _pageItemList)
        {
            if (item.pageIndex == pageIndex)
            {
                return item;
            }
        }
        return null;
    }
    
    public void PageTo(int pageIndex)
    {
        if (pageIndex < 0 || pageIndex >= _pageItemList.Count) return;
        
        if (_curPageIndex != pageIndex)
        {
            _curPageIndex = pageIndex;
            _curSelectedItem = GetPageItem(_curPageIndex);
            if (_curSelectedItem != null)
            {
                _endPos = -_curSelectedItem.GetComponent<RectTransform>().anchoredPosition;
                _smoothMoving = true;
                _curPageIndex = _curSelectedItem.pageIndex;
                onPageChanged.Invoke(_curPageIndex);
            }
        }
    }
}